# k近邻回归

"""
 k近邻算法不仅能用于分类，还能用于回归问题。
 以wave数据集为例，我们从单一近邻回归开始，并添加了3个用绿色五角星标记的测试点。
 在这种情况下，模型会使用最近的邻居（即距离最近的点）的目标值作为预测结果。

"""
import mglearn
import numpy as np
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split

# plot_knn_regression 函数来绘制 k近邻回归模型的可视化图。
mglearn.plots.plot_knn_regression(n_neighbors=1)
# plt.show()

# 可以用多个近邻进行回归。在使用多个近邻时，预测结果为这些邻居的平均值
mglearn.plots.plot_knn_regression(n_neighbors=3)
# plt.show()

# 用于回归的 k 近邻算法在 scikit-learn 的 KNeighborsRegressor 类中实现。其用法与
# KNeighborsClassifier 类似
from sklearn.neighbors import KNeighborsRegressor

X, y = mglearn.datasets.make_wave(n_samples=40)
# 将wave数据集分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
# 模型实例化，并将邻居个数设为3
reg = KNeighborsRegressor(n_neighbors=3)
# 利用训练数据和训练目标值来拟合模型
reg.fit(X_train, y_train)

print(f"Test set prediction:\n{reg.predict(X_test)}")

# 我们还可以用 score 方法来评估模型，对于回归问题，这一方法返回的是 R2 分数。R2 分
# 数也叫作决定系数，是回归模型预测的优度度量，位于 0 到 1 之间。R2 等于 1 对应完美预
# 测，R2 等于 0 对应常数模型，即总是预测训练集响应（y_train）的平均值：
print("Test set R^2: {:.2f}".format(reg.score(X_test, y_test)))
# 这里的分数是 0.83，表示模型的拟合相对较好。

# 分析KNeighborsRegressor
# 对于我们的一维数据集，可以查看所有特征取值对应的预测结果。为了便于绘图，
# 我们创建一个由许多点组成的测试数据集：
# 创建1000个数据点，在-3和3之间均匀分布

# 使用 NumPy 库来生成一个线性空间（linspace）的数组，并将其重塑为一维数组
# np.linspace(-3, 3, 1000)：这个函数生成一个从-3到3的等间隔的数字数组，包括起始值-3和结束值3，总共生成1000个数字
line = np.linspace(-3, 3, 1000).reshape(-1, 1)

fig, axes = plt.subplots(1, 3, figsize=(10, 3))

for n_neighbors, ax in zip([1, 3, 9], axes):
    # 利用1个、3个或9个邻居分别进行预测
    reg = KNeighborsRegressor(n_neighbors=n_neighbors)
    reg.fit(X_train, y_train)
    ax.plot(line, reg.predict(line))
    # c=mglearn.cm2(0): 这个参数指定了数据点的颜色。
    # markersize=8: 这个参数指定了数据点标记的大小。在这里，标记的大小被设置为8，单位通常是点（pt）
    ax.plot(X_train, y_train, '^', c=mglearn.cm2(0), markersize=8)
    ax.plot(X_test, y_test, 'v', c=mglearn.cm2(1), markersize=8)
    ax.set_title(
        "{} neighbor(s)\n train score: {:.2f} test score: {:.2f}".format(
            n_neighbors, reg.score(X_train, y_train),
            reg.score(X_test, y_test)))
    ax.set_xlabel("Feature")
    ax.set_ylabel("Target")
axes[0].legend(["Model predictions", "Training data/target",
                "Test data/target"], loc="best")

plt.show()
'''
 使用单一邻居时，每个训练点都极大影响预测，导致预测曲线穿过所有数据点，非常不稳定。
 增加邻居数量后，预测曲线更平滑，但对训练数据的拟合变差。
'''

'''
 k-NN（KNeighbors）分类器的优点、缺点和参数

 重要参数：
 邻居个数：k-NN算法中最重要的参数之一，通常较小的值（如3或5）效果较好，但需要根据具体情况调整。
 距离度量方法：默认使用欧氏距离，适用于多数情况，但选择合适的度量方法对模型性能有影响。

 优点：
 易于理解：k-NN模型直观易懂，通常不需要复杂的调参就能获得不错的性能。
 快速构建：构建k-NN模型的速度通常很快。

 缺点：
 预测速度慢：当训练集很大时，k-NN的预测速度可能会变慢。
 对数据预处理敏感：k-NN对数据预处理的要求较高，对特征缩放和归一化敏感。
 不适用于高维数据：对于特征数量很多的数据集，k-NN效果不佳，尤其是在稀疏数据集上表现更差。

 实际应用限制：
 尽管k-NN易于理解，但由于其在预测速度和处理高维数据集方面的局限性，实际应用中可能不常使用。
'''
